1 <?php
2 ########################################################################
3 /*
4 ~~~~~~ LIST OF FUNCTIONS ~~~~~~
5 getTableList() -- returns an associative array of all tables in this application in the format tableName=>tableCaption
6 getThumbnailSpecs($tableName, $fieldName, $view) -- returns an associative array specifying the width, height and identifier of the thumbnail file.
7 createThumbnail($img, $specs) -- $specs is an array as returned by getThumbnailSpecs(). Returns true on success, false on failure.
8 makeSafe($string)
9 checkPermissionVal($pvn)
10 sql($statment, $o)
11 sqlValue($statment)
12 getLoggedAdmin()
13 checkUser($username, $password)
14 logOutUser()
15 getPKFieldName($tn)
16 getCSVData($tn, $pkValue, $stripTag=true)
17 errorMsg($msg)
18 redirect($URL, $absolute=FALSE)
19 htmlRadioGroup($name, $arrValue, $arrCaption, $selectedValue, $selClass="", $class="", $separator="<br>")
20 htmlSelect($name, $arrValue, $arrCaption, $selectedValue, $class="", $selectedClass="")
21 htmlSQLSelect($name, $sql, $selectedValue, $class="", $selectedClass="")
22 isEmail($email) -- returns $email if valid or false otherwise.
23 notifyMemberApproval($memberID) -- send an email to member acknowledging his approval by admin, returns false if no mail is sent
24 setupMembership() -- check if membership tables exist or not. If not, create them.
25 thisOr($this_val, $or) -- return $this_val if it has a value, or $or if not.
26 getUploadedFile($FieldName, $MaxSize=0, $FileTypes='csv|txt', $NoRename=false, $dir='')
27 toBytes($val)
28 convertLegacyOptions($CSVList)
29 getValueGivenCaption($query, $caption)
30 undo_magic_quotes($str)
31 time24($t) -- return time in 24h format
32 time12($t) -- return time in 12h format
33 application_url($page) -- return absolute URL of provided page
34 is_ajax() -- return true if this is an ajax request, false otherwise
35 array_trim($arr) -- recursively trim provided value/array
36 is_allowed_username($username, $exception = false) -- returns username if valid and unique, or false otherwise (if exception is provided and same as username, no uniqueness check is performed)
37 csrf_token($validate) -- csrf-proof a form
38 get_plugins() -- scans for installed plugins and returns them in an array ('name', 'title', 'icon' or 'glyphicon', 'admin_path')
39 maintenance_mode($new_status = '') -- retrieves (and optionally sets) maintenance mode status
40 html_attr($str) -- prepare $str to be placed inside an HTML attribute
41 Request($var) -- class for providing sanitized values of given request variable (->sql, ->attr, ->html, ->url, and ->raw)
42 Notification() -- class for providing a standardized html notifications functionality
43 sendmail($mail) -- sends an email using PHPMailer as specified in the assoc array $mail( ['to', 'name', 'subject', 'message', 'debug'] ) and returns true on success or an error message on failure
44 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45 */
46 ########################################################################
47
48 #########################################################
49 if(!function_exists('getTableList')){
50 function getTableList($skip_authentication = false){
51 $arrTables = array(
52 'patients' => 'Patients',
53 'disease_symptoms' => 'Disease symptoms',
54 'medical_records' => 'Medical Records',
55 'events' => 'Appointments'
56 );
57
58 return $arrTables;
59 }
60 }
61 ########################################################################
62 function getThumbnailSpecs($tableName, $fieldName, $view){
63 if($tableName=='patients' && $fieldName=='image' && $view=='tv')
64 return array('width'=>50, 'height'=>50, 'identifier'=>'_tv');
65 elseif($tableName=='patients' && $fieldName=='image' && $view=='dv')
66 return array('width'=>250, 'height'=>250, 'identifier'=>'_dv');
67 elseif($tableName=='medical_records' && $fieldName=='image_1' && $view=='tv')
68 return array('width'=>50, 'height'=>50, 'identifier'=>'_tv');
69 elseif($tableName=='medical_records' && $fieldName=='image_1' && $view=='dv')
70 return array('width'=>250, 'height'=>250, 'identifier'=>'_dv');
71 elseif($tableName=='medical_records' && $fieldName=='image_2' && $view=='tv')
72 return array('width'=>50, 'height'=>50, 'identifier'=>'_tv');
73 elseif($tableName=='medical_records' && $fieldName=='image_2' && $view=='dv')
74 return array('width'=>250, 'height'=>250, 'identifier'=>'_dv');
75 elseif($tableName=='medical_records' && $fieldName=='image_3' && $view=='tv')
76 return array('width'=>50, 'height'=>50, 'identifier'=>'_tv');
77 elseif($tableName=='medical_records' && $fieldName=='image_3' && $view=='dv')
78 return array('width'=>250, 'height'=>250, 'identifier'=>'_dv');
79 elseif($tableName=='medical_records' && $fieldName=='image_4' && $view=='tv')
80 return array('width'=>50, 'height'=>50, 'identifier'=>'_tv');
81 elseif($tableName=='medical_records' && $fieldName=='image_4' && $view=='dv')
82 return array('width'=>250, 'height'=>250, 'identifier'=>'_dv');
83 elseif($tableName=='medical_records' && $fieldName=='image_5' && $view=='tv')
84 return array('width'=>50, 'height'=>50, 'identifier'=>'_tv');
85 elseif($tableName=='medical_records' && $fieldName=='image_5' && $view=='dv')
86 return array('width'=>250, 'height'=>250, 'identifier'=>'_dv');
87 return FALSE;
88 }
89 ########################################################################
90 function createThumbnail($img, $specs){
91 $w=$specs['width'];
92 $h=$specs['height'];
93 $id=$specs['identifier'];
94 $path=dirname($img);
95
96 // image doesn't exist or inaccessible?
97 if(!$size=@getimagesize($img)) return FALSE;
98
99 // calculate thumbnail size to maintain aspect ratio
100 $ow=$size[0]; // original image width
101 $oh=$size[1]; // original image height
102 $twbh=$h/$oh*$ow; // calculated thumbnail width based on given height
103 $thbw=$w/$ow*$oh; // calculated thumbnail height based on given width
104 if($w && $h){
105 if($twbh>$w) $h=$thbw;
106 if($thbw>$h) $w=$twbh;
107 }elseif($w){
108 $h=$thbw;
109 }elseif($h){
110 $w=$twbh;
111 }else{
112 return FALSE;
113 }
114
115 // dir not writeable?
116 if(!is_writable($path)) return FALSE;
117
118 // GD lib not loaded?
119 if(!function_exists('gd_info')) return FALSE;
120 $gd=gd_info();
121
122 // GD lib older than 2.0?
123 preg_match('/\d/', $gd['GD Version'], $gdm);
124 if($gdm[0]<2) return FALSE;
125
126 // get file extension
127 preg_match('/\.[a-zA-Z]{3,4}$/U', $img, $matches);
128 $ext=strtolower($matches[0]);
129
130 // check if supplied image is supported and specify actions based on file type
131 if($ext=='.gif'){
132 if(!$gd['GIF Create Support']) return FALSE;
133 $thumbFunc='imagegif';
134 }elseif($ext=='.png'){
135 if(!$gd['PNG Support']) return FALSE;
136 $thumbFunc='imagepng';
137 }elseif($ext=='.jpg' || $ext=='.jpe' || $ext=='.jpeg'){
138 if(!$gd['JPG Support'] && !$gd['JPEG Support']) return FALSE;
139 $thumbFunc='imagejpeg';
140 }else{
141 return FALSE;
142 }
143
144 // determine thumbnail file name
145 $ext=$matches[0];
146 $thumb=substr($img, 0, -5).str_replace($ext, $id.$ext, substr($img, -5));
147
148 // if the original image smaller than thumb, then just copy it to thumb
149 if($h>$oh && $w>$ow){
150 return (@copy($img, $thumb) ? TRUE : FALSE);
151 }
152
153 // get image data
154 if(!$imgData=imagecreatefromstring(implode('', file($img)))) return FALSE;
155
156 // finally, create thumbnail
157 $thumbData=imagecreatetruecolor($w, $h);
158
159 //preserve transparency of png and gif images
160 if($thumbFunc=='imagepng'){
161 if(($clr=@imagecolorallocate($thumbData, 0, 0, 0))!=-1){
162 @imagecolortransparent($thumbData, $clr);
163 @imagealphablending($thumbData, false);
164 @imagesavealpha($thumbData, true);
165 }
166 }elseif($thumbFunc=='imagegif'){
167 @imagealphablending($thumbData, false);
168 $transIndex=imagecolortransparent($imgData);
169 if($transIndex>=0){
170 $transClr=imagecolorsforindex($imgData, $transIndex);
171 $transIndex=imagecolorallocatealpha($thumbData, $transClr['red'], $transClr['green'], $transClr['blue'], 127);
172 imagefill($thumbData, 0, 0, $transIndex);
173 }
174 }
175
176 // resize original image into thumbnail
177 if(!imagecopyresampled($thumbData, $imgData, 0, 0 , 0, 0, $w, $h, $ow, $oh)) return FALSE;
178 unset($imgData);
179
180 // gif transparency
181 if($thumbFunc=='imagegif' && $transIndex>=0){
182 imagecolortransparent($thumbData, $transIndex);
183 for($y=0; $y<$h; ++$y)
184 for($x=0; $x<$w; ++$x)
185 if(((imagecolorat($thumbData, $x, $y)>>24) & 0x7F) >= 100) imagesetpixel($thumbData, $x, $y, $transIndex);
186 imagetruecolortopalette($thumbData, true, 255);
187 imagesavealpha($thumbData, false);
188 }
189
190 if(!$thumbFunc($thumbData, $thumb)) return FALSE;
191 unset($thumbData);
192
193 return TRUE;
194 }
195 ########################################################################
196 function makeSafe($string, $is_gpc = true){
197 if($is_gpc) $string = (get_magic_quotes_gpc() ? stripslashes($string) : $string);
198 if(!db_link()){ sql("select 1+1", $eo); }
199 return db_escape($string);
200 }
201 ########################################################################
202 function checkPermissionVal($pvn){
203 // fn to make sure the value in the given POST variable is 0, 1, 2 or 3
204 // if the value is invalid, it default to 0
205 $pvn=intval($_POST[$pvn]);
206 if($pvn!=1 && $pvn!=2 && $pvn!=3){
207 return 0;
208 }else{
209 return $pvn;
210 }
211 }
212 ########################################################################
213 if(!function_exists('sql')){
214 function sql($statment, &$o){
215
216 /*
217 Supported options that can be passed in $o options array (as array keys):
218 'silentErrors': If true, errors will be returned in $o['error'] rather than displaying them on screen and exiting.
219 */
220
221 global $Translation;
222 static $connected = false, $db_link;
223
224 $dbServer = config('dbServer');
225 $dbUsername = config('dbUsername');
226 $dbPassword = config('dbPassword');
227 $dbDatabase = config('dbDatabase');
228
229 ob_start();
230
231 if(!$connected){
232 /****** Connect to MySQL ******/
233 if(!extension_loaded('mysql') && !extension_loaded('mysqli')){
234 echo Notification::placeholder();
235 echo Notification::show(array(
236 'message' => 'PHP is not configured to connect to MySQL on this machine. Please see <a href="http://www.php.net/manual/en/ref.mysql.php">this page</a> for help on how to configure MySQL.',
237 'class' => 'danger',
238 'dismiss_seconds' => 7200
239 ));
240 $e=ob_get_contents(); ob_end_clean(); if($o['silentErrors']){ $o['error']=$e; return FALSE; }else{ echo $e; exit; }
241 }
242
243 if(!($db_link = @db_connect($dbServer, $dbUsername, $dbPassword))){
244 echo Notification::placeholder();
245 echo Notification::show(array(
246 'message' => db_error($db_link, true),
247 'class' => 'danger',
248 'dismiss_seconds' => 7200
249 ));
250 $e=ob_get_contents(); ob_end_clean(); if($o['silentErrors']){ $o['error']=$e; return FALSE; }else{ echo $e; exit; }
251 }
252
253 /****** Select DB ********/
254 if(!db_select_db($dbDatabase, $db_link)){
255 echo Notification::placeholder();
256 echo Notification::show(array(
257 'message' => db_error($db_link),
258 'class' => 'danger',
259 'dismiss_seconds' => 7200
260 ));
261 $e=ob_get_contents(); ob_end_clean(); if($o['silentErrors']){ $o['error']=$e; return FALSE; }else{ echo $e; exit; }
262 }
263
264 $connected = true;
265 }
266
267 if(!$result = @db_query($statment, $db_link)){
268 if(!stristr($statment, "show columns")){
269 // retrieve error codes
270 $errorNum = db_errno($db_link);
271 $errorMsg = htmlspecialchars(db_error($db_link));
272
273 if(getLoggedAdmin()) $errorMsg .= "<pre>{$Translation['query:']}\n" . htmlspecialchars($statment) . "</pre><i class=\"text-right\">{$Translation['admin-only info']}</i>";
274
275 echo Notification::placeholder();
276 echo Notification::show(array(
277 'message' => $errorMsg,
278 'class' => 'danger',
279 'dismiss_seconds' => 7200
280 ));
281 $e = ob_get_contents(); ob_end_clean(); if($o['silentErrors']){ $o['error'] = $errorMsg; return false; }else{ echo $e; exit; }
282 }
283 }
284
285 ob_end_clean();
286 return $result;
287 }
288 }
289 ########################################################################
290 function sqlValue($statment){
291 // executes a statment that retreives a single data value and returns the value retrieved
292 if(!$res=sql($statment, $eo)){
293 return FALSE;
294 }
295 if(!$row=db_fetch_row($res)){
296 return FALSE;
297 }
298 return $row[0];
299 }
300 ########################################################################
301 function getLoggedAdmin(){
302 // checks session variables to see whether the admin is logged or not
303 // if not, it returns FALSE
304 // if logged, it returns the user id
305
306 $adminConfig = config('adminConfig');
307
308 if($_SESSION['adminUsername']!=''){
309 return $_SESSION['adminUsername'];
310 }elseif($_SESSION['memberID']==$adminConfig['adminUsername']){
311 $_SESSION['adminUsername']=$_SESSION['memberID'];
312 return $_SESSION['adminUsername'];
313 }else{
314 return FALSE;
315 }
316 }
317 ########################################################################
318 function checkUser($username, $password){
319 // checks given username and password for validity
320 // if valid, registers the username in a session and returns true
321 // else, return FALSE and destroys session
322
323 $adminConfig = config('adminConfig');
324 if($username != $adminConfig['adminUsername'] || md5($password) != $adminConfig['adminPassword']){
325 return FALSE;
326 }
327
328 $_SESSION['adminUsername'] = $username;
329 $_SESSION['memberGroupID'] = sqlValue("select groupID from membership_users where memberID='" . makeSafe($username) ."'");
330 $_SESSION['memberID'] = $username;
331 return TRUE;
332 }
333 ########################################################################
334 function logOutUser(){
335 // destroys current session
336 $_SESSION = array();
337 if(isset($_COOKIE[session_name()])){
338 setcookie(session_name(), '', time()-42000, '/');
339 }
340 if(isset($_COOKIE['online_clinic_management_system_rememberMe'])){
341 setcookie('online_clinic_management_system_rememberMe', '', time()-42000);
342 }
343 session_destroy();
344 }
345 ########################################################################
346 function getPKFieldName($tn){
347 // get pk field name of given table
348
349 $stn = makeSafe($tn, false);
350 if(!$res = sql("show fields from `$stn`", $eo)){
351 return false;
352 }
353
354 while($row = db_fetch_assoc($res)){
355 if($row['Key'] == 'PRI'){
356 return $row['Field'];
357 }
358 }
359
360 return false;
361 }
362 ########################################################################
363 function getCSVData($tn, $pkValue, $stripTags=true){
364 // get pk field name for given table
365 if(!$pkField=getPKFieldName($tn)){
366 return "";
367 }
368
369 // get a concat string to produce a csv list of field values for given table record
370 if(!$res=sql("show fields from `$tn`", $eo)){
371 return "";
372 }
373 while($row=db_fetch_assoc($res)){
374 $csvFieldList.="`{$row['Field']}`,";
375 }
376 $csvFieldList=substr($csvFieldList, 0, -1);
377
378 $csvData=sqlValue("select CONCAT_WS(', ', $csvFieldList) from `$tn` where `$pkField`='" . makeSafe($pkValue, false) . "'");
379
380 return ($stripTags ? strip_tags($csvData) : $csvData);
381 }
382 ########################################################################
383 function errorMsg($msg){
384 echo "<div class=\"alert alert-danger\">{$msg}</div>";
385 }
386 ########################################################################
387 function redirect($URL, $absolute=FALSE){
388 $fullURL = ($absolute ? $URL : application_url($URL));
389 if(!headers_sent()) header("Location: $fullURL");
390
391 echo "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0;url=$fullURL\">";
392 echo "<br><br><a href=\"$fullURL\">Click here</a> if you aren't automatically redirected.";
393 exit;
394 }
395 ########################################################################
396 function htmlRadioGroup($name, $arrValue, $arrCaption, $selectedValue, $selClass = "text-primary", $class = "", $separator = "<br>"){
397 if(!is_array($arrValue)) return '';
398
399 ob_start();
400 ?>
401 <div class="radio %%CLASS%%"><label>
402 <input type="radio" name="%%NAME%%" id="%%ID%%" value="%%VALUE%%" %%CHECKED%%> %%LABEL%%
403 </label></div>
404 <?php
405 $template = ob_get_contents();
406 ob_end_clean();
407
408 $out = '';
409 for($i = 0; $i < count($arrValue); $i++){
410 $replacements = array(
411 '%%CLASS%%' => html_attr($arrValue[$i] == $selectedValue ? $selClass :$class),
412 '%%NAME%%' => html_attr($name),
413 '%%ID%%' => html_attr($name . $i),
414 '%%VALUE%%' => html_attr($arrValue[$i]),
415 '%%LABEL%%' => $arrCaption[$i],
416 '%%CHECKED%%' => ($arrValue[$i]==$selectedValue ? " checked" : "")
417 );
418 $out .= str_replace(array_keys($replacements), array_values($replacements), $template);
419 }
420
421 return $out;
422 }
423 ########################################################################
424 function htmlSelect($name, $arrValue, $arrCaption, $selectedValue, $class="", $selectedClass=""){
425 if($selectedClass==""){
426 $selectedClass=$class;
427 }
428 if(is_array($arrValue)){
429 $out="<select name=\"$name\" id=\"$name\">";
430 for($i=0; $i<count($arrValue); $i++){
431 $out.="<option value=\"".$arrValue[$i]."\"".($arrValue[$i]==$selectedValue ? " selected class=\"$class\"" : " class=\"$selectedClass\"").">".$arrCaption[$i]."</option>";
432 }
433 $out.="</select>";
434 }
435 return $out;
436 }
437 ########################################################################
438 function htmlSQLSelect($name, $sql, $selectedValue, $class="", $selectedClass=""){
439 $arrVal[]='';
440 $arrCap[]='';
441 if($res=sql($sql, $eo)){
442 while($row=db_fetch_row($res)){
443 $arrVal[]=$row[0];
444 $arrCap[]=$row[1];
445 }
446 return htmlSelect($name, $arrVal, $arrCap, $selectedValue, $class, $selectedClass);
447 }else{
448 return "";
449 }
450 }
451 ########################################################################
452 function bootstrapSelect($name, $arrValue, $arrCaption, $selectedValue, $class = '', $selectedClass = ''){
453 if($selectedClass == '') $selectedClass = $class;
454
455 $out = "<select class=\"form-control\" name=\"{$name}\" id=\"{$name}\">";
456 if(is_array($arrValue)){
457 for($i = 0; $i < count($arrValue); $i++){
458 $selected = "class=\"{$class}\"";
459 if($arrValue[$i] == $selectedValue) $selected = "selected class=\"{$selectedClass}\"";
460 $out .= "<option value=\"{$arrValue[$i]}\" {$selected}>{$arrCaption[$i]}</option>";
461 }
462 }
463 $out .= '</select>';
464
465 return $out;
466 }
467 ########################################################################
468 function bootstrapSQLSelect($name, $sql, $selectedValue, $class = '', $selectedClass = ''){
469 $arrVal[] = '';
470 $arrCap[] = '';
471 if($res = sql($sql, $eo)){
472 while($row = db_fetch_row($res)){
473 $arrVal[] = $row[0];
474 $arrCap[] = $row[1];
475 }
476 return bootstrapSelect($name, $arrVal, $arrCap, $selectedValue, $class, $selectedClass);
477 }
478
479 return '';
480 }
481 ########################################################################
482 function isEmail($email){
483 if(preg_match('/^([*+!.&#$¦\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,45})$/i', $email)){
484 return $email;
485 }else{
486 return FALSE;
487 }
488 }
489 ########################################################################
490 function notifyMemberApproval($memberID){
491 $adminConfig = config('adminConfig');
492 $memberID = strtolower($memberID);
493
494 $email = sqlValue("select email from membership_users where lcase(memberID)='{$memberID}'");
495
496 return sendmail(array(
497 'to' => $email,
498 'name' => $memberID,
499 'subject' => $adminConfig['approvalSubject'],
500 'message' => nl2br($adminConfig['approvalMessage'])
501 ));
502 }
503 ########################################################################
504 function setupMembership(){
505 // run once per request
506 static $executed = false;
507 if($executed) return;
508 $executed = true;
509
510 /* abort if current page is one of the following exceptions */
511 $exceptions = array('pageEditMember.php', 'membership_passwordReset.php', 'membership_profile.php', 'membership_signup.php', 'pageChangeMemberStatus.php', 'pageDeleteGroup.php', 'pageDeleteMember.php', 'pageEditGroup.php', 'pageEditMemberPermissions.php', 'pageRebuildFields.php', 'pageSettings.php');
512 if(in_array(basename($_SERVER['PHP_SELF']), $exceptions)) return;
513
514 $eo = array('silentErrors' => true);
515
516 $adminConfig = config('adminConfig');
517 $today = @date('Y-m-d');
518
519 $membership_tables = array(
520 'membership_groups' => "CREATE TABLE IF NOT EXISTS membership_groups (groupID int unsigned NOT NULL auto_increment, name varchar(20), description text, allowSignup tinyint, needsApproval tinyint, PRIMARY KEY (groupID)) CHARSET " . mysql_charset,
521 'membership_users' => "CREATE TABLE IF NOT EXISTS membership_users (memberID varchar(20) NOT NULL, passMD5 varchar(40), email varchar(100), signupDate date, groupID int unsigned, isBanned tinyint, isApproved tinyint, custom1 text, custom2 text, custom3 text, custom4 text, comments text, PRIMARY KEY (memberID)) CHARSET " . mysql_charset,
522 'membership_grouppermissions' => "CREATE TABLE IF NOT EXISTS membership_grouppermissions (permissionID int unsigned NOT NULL auto_increment, groupID int, tableName varchar(100), allowInsert tinyint, allowView tinyint NOT NULL DEFAULT '0', allowEdit tinyint NOT NULL DEFAULT '0', allowDelete tinyint NOT NULL DEFAULT '0', PRIMARY KEY (permissionID)) CHARSET " . mysql_charset,
523 'membership_userrecords' => "CREATE TABLE IF NOT EXISTS membership_userrecords (recID bigint unsigned NOT NULL auto_increment, tableName varchar(100), pkValue varchar(255), memberID varchar(20), dateAdded bigint unsigned, dateUpdated bigint unsigned, groupID int, PRIMARY KEY (recID)) CHARSET " . mysql_charset,
524 'membership_userpermissions' => "CREATE TABLE IF NOT EXISTS membership_userpermissions (permissionID int unsigned NOT NULL auto_increment, memberID varchar(20) NOT NULL, tableName varchar(100), allowInsert tinyint, allowView tinyint NOT NULL DEFAULT '0', allowEdit tinyint NOT NULL DEFAULT '0', allowDelete tinyint NOT NULL DEFAULT '0', PRIMARY KEY (permissionID)) CHARSET " . mysql_charset
525 );
526
527 // get db tables
528 $tables = array();
529 $res = sql("show tables", $eo);
530
531 if(!$res){
532 include_once(dirname(__FILE__) . '/../header.php');
533 echo $eo['error'];
534 include_once(dirname(__FILE__) . '/../footer.php');
535 exit;
536 }
537
538 while($row = db_fetch_array($res)) $tables[] = $row[0];
539
540 // check if membership tables exist or not
541 foreach($membership_tables as $tn => $tdef){
542 if(!in_array($tn, $tables)){
543 sql($tdef, $eo);
544 }
545 }
546
547 // check membership_users definition
548 $membership_users = array();
549 $res = sql("show columns from membership_users", $eo);
550 while($row = db_fetch_assoc($res)) $membership_users[$row['Field']] = $row;
551
552 if(!in_array('pass_reset_key', array_keys($membership_users))) @db_query("ALTER TABLE membership_users ADD COLUMN pass_reset_key VARCHAR(100)");
553 if(!in_array('pass_reset_expiry', array_keys($membership_users))) @db_query("ALTER TABLE membership_users ADD COLUMN pass_reset_expiry INT UNSIGNED");
554 if(!$membership_users['groupID']['Key']) @db_query("ALTER TABLE membership_users ADD INDEX groupID (groupID)");
555
556 // create membership indices if not existing
557 $membership_userrecords = array();
558 $res = sql("show keys from membership_userrecords", $eo);
559 while($row = db_fetch_assoc($res)) $membership_userrecords[$row['Key_name']][$row['Seq_in_index']] = $row;
560
561 if(!$membership_userrecords['pkValue'][1]) @db_query("ALTER TABLE membership_userrecords ADD INDEX pkValue (pkValue)");
562 if(!$membership_userrecords['tableName'][1]) @db_query("ALTER TABLE membership_userrecords ADD INDEX tableName (tableName)");
563 if(!$membership_userrecords['memberID'][1]) @db_query("ALTER TABLE membership_userrecords ADD INDEX memberID (memberID)");
564 if(!$membership_userrecords['groupID'][1]) @db_query("ALTER TABLE membership_userrecords ADD INDEX groupID (groupID)");
565 if(!$membership_userrecords['tableName_pkValue'][1] || !$membership_userrecords['tableName_pkValue'][2]) @db_query("ALTER IGNORE TABLE membership_userrecords ADD UNIQUE INDEX tableName_pkValue (tableName, pkValue)");
566
567 // retreive anonymous and admin groups and their permissions
568 $anon_group = $adminConfig['anonymousGroup'];
569 $anon_user = strtolower($adminConfig['anonymousMember']);
570 $admin_group = 'Admins';
571 $admin_user = strtolower($adminConfig['adminUsername']);
572 $groups_permissions = array();
573 $res = sql(
574 "select g.groupID, g.name, gp.tableName, gp.allowInsert, gp.allowView, gp.allowEdit, gp.allowDelete " .
575 "from membership_groups g left join membership_grouppermissions gp on g.groupID=gp.groupID " .
576 "where g.name='" . makeSafe($admin_group) . "' or g.name='" . makeSafe($anon_group) . "' " .
577 "order by g.groupID, gp.tableName", $eo
578 );
579 while($row = db_fetch_assoc($res)) $groups_permissions[] = $row;
580
581 // check anonymous group and user and create if necessary
582 $anon_group_id = false;
583 foreach($groups_permissions as $group){
584 if($group['name'] == $anon_group){
585 $anon_group_id = $group['groupID'];
586 break;
587 }
588 }
589
590 if(!$anon_group_id){
591 sql("insert into membership_groups set name='" . makeSafe($anon_group) . "', allowSignup=0, needsApproval=0, description='Anonymous group created automatically on " . @date("Y-m-d") . "'", $eo);
592 $anon_group_id = db_insert_id();
593 }
594
595 if($anon_group_id){
596 $anon_user_db = sqlValue("select lcase(memberID) from membership_users where lcase(memberID)='" . makeSafe($anon_user) . "' and groupID='{$anon_group_id}'");
597 if(!$anon_user_db || $anon_user_db != $anon_user){
598 sql("delete from membership_users where groupID='{$anon_group_id}'", $eo);
599 sql("insert into membership_users set memberID='" . makeSafe($anon_user) . "', signUpDate='{$today}', groupID='{$anon_group_id}', isBanned=0, isApproved=1, comments='Anonymous member created automatically on {$today}'", $eo);
600 }
601 }
602
603 // check admin group and user and create if necessary
604 $admin_group_id = false;
605 foreach($groups_permissions as $group){
606 if($group['name'] == $admin_group){
607 $admin_group_id = $group['groupID'];
608 break;
609 }
610 }
611
612 if(!$admin_group_id){
613 sql("insert into membership_groups set name='" . makeSafe($admin_group) . "', allowSignup=0, needsApproval=1, description='Admin group created automatically on {$today}'", $eo);
614 $admin_group_id = db_insert_id();
615 }
616
617 if($admin_group_id){
618 // check that admins can access all tables
619 $all_tables = getTableList(true);
620 $tables_ok = $perms_ok = array();
621 foreach($all_tables as $tn => $tc) $tables_ok[$tn] = $perms_ok[$tn] = false;
622
623 foreach($groups_permissions as $group){
624 if($group['name'] == $admin_group){
625 if(isset($tables_ok[$group['tableName']])){
626 $tables_ok[$group['tableName']] = true;
627 if($group['allowInsert'] == 1 && $group['allowDelete'] == 3 && $group['allowEdit'] == 3 && $group['allowView'] == 3){
628 $perms_ok[$group['tableName']] = true;
629 }
630 }
631 }
632 }
633
634 // if any table has no record in Admins permissions, create one for it
635 $grant_sql = array();
636 foreach($tables_ok as $tn => $status){
637 if(!$status) $grant_sql[] = "({$admin_group_id}, '{$tn}')";
638 }
639
640 if(count($grant_sql)){
641 sql("insert into membership_grouppermissions (groupID, tableName) values " . implode(',', $grant_sql), $eo);
642 }
643
644 // check admin permissions and update if necessary
645 $perms_sql = array();
646 foreach($perms_ok as $tn => $status){
647 if(!$status) $perms_sql[] = "'{$tn}'";
648 }
649
650 if(count($perms_sql)){
651 sql("update membership_grouppermissions set allowInsert=1, allowView=3, allowEdit=3, allowDelete=3 where groupID={$admin_group_id} and tableName in (" . implode(',', $perms_sql) . ")", $eo);
652 }
653
654 // check if super admin is stored in the users table and add him if not
655 $admin_user_exists = sqlValue("select count(1) from membership_users where lcase(memberID)='" . makeSafe($admin_user)."' and groupID='{$admin_group_id}'");
656 if(!$admin_user_exists){
657 sql("insert into membership_users set memberID='" . makeSafe($admin_user) . "', passMD5='{$adminConfig['adminPassword']}', email='{$adminConfig['senderEmail']}', signUpDate='{$today}', groupID='{$admin_group_id}', isBanned=0, isApproved=1, comments='Admin member created automatically on {$today}'", $eo);
658 }
659 }
660 }
661
662 ########################################################################
663 function thisOr($this_val, $or = ' '){
664 return ($this_val != '' ? $this_val : $or);
665 }
666 ########################################################################
667 function getUploadedFile($FieldName, $MaxSize=0, $FileTypes='csv|txt', $NoRename=false, $dir=''){
668 $currDir=dirname(__FILE__);
669 if(is_array($_FILES)){
670 $f = $_FILES[$FieldName];
671 }else{
672 return 'Your php settings don\'t allow file uploads.';
673 }
674
675 if(!$MaxSize){
676 $MaxSize=toBytes(ini_get('upload_max_filesize'));
677 }
678
679 if(!is_dir("$currDir/csv")){
680 @mkdir("$currDir/csv");
681 }
682
683 $dir=(is_dir($dir) && is_writable($dir) ? $dir : "$currDir/csv/");
684
685 if($f['error']!=4 && $f['name']!=''){
686 if($f['size']>$MaxSize || $f['error']){
687 return 'File size exceeds maximum allowed of '.intval($MaxSize / 1024).'KB';
688 }
689 if(!preg_match('/\.('.$FileTypes.')$/i', $f['name'], $ft)){
690 return 'File type not allowed. Only these file types are allowed: '.str_replace('|', ', ', $FileTypes);
691 }
692
693 if($NoRename){
694 $n = str_replace(' ', '_', $f['name']);
695 }else{
696 $n = microtime();
697 $n = str_replace(' ', '_', $n);
698 $n = str_replace('0.', '', $n);
699 $n .= $ft[0];
700 }
701
702 if(!@move_uploaded_file($f['tmp_name'], $dir . $n)){
703 return 'Couldn\'t save the uploaded file. Try chmoding the upload folder "'.$dir.'" to 777.';
704 }else{
705 @chmod($dir.$n, 0666);
706 return $dir.$n;
707 }
708 }
709 return 'An error occured while uploading the file. Please try again.';
710 }
711 ########################################################################
712 function toBytes($val){
713 $val = trim($val);
714 $last = strtolower($val{strlen($val)-1});
715 switch($last){
716 // The 'G' modifier is available since PHP 5.1.0
717 case 'g':
718 $val *= 1024;
719 case 'm':
720 $val *= 1024;
721 case 'k':
722 $val *= 1024;
723 }
724
725 return $val;
726 }
727 ########################################################################
728 function convertLegacyOptions($CSVList){
729 $CSVList=str_replace(';;;', ';||', $CSVList);
730 $CSVList=str_replace(';;', '||', $CSVList);
731 return $CSVList;
732 }
733 ########################################################################
734 function getValueGivenCaption($query, $caption){
735 if(!preg_match('/select\s+(.*?)\s*,\s*(.*?)\s+from\s+(.*?)\s+order by.*/i', $query, $m)){
736 if(!preg_match('/select\s+(.*?)\s*,\s*(.*?)\s+from\s+(.*)/i', $query, $m)){
737 return '';
738 }
739 }
740
741 // get where clause if present
742 if(preg_match('/\s+from\s+(.*?)\s+where\s+(.*?)\s+order by.*/i', $query, $mw)){
743 $where="where ($mw[2]) AND";
744 $m[3]=$mw[1];
745 }else{
746 $where='where';
747 }
748
749 $caption=makeSafe($caption);
750 return sqlValue("SELECT $m[1] FROM $m[3] $where $m[2]='$caption'");
751 }
752 ########################################################################
753 function undo_magic_quotes($str){
754 return (get_magic_quotes_gpc() ? stripslashes($str) : $str);
755 }
756 ########################################################################
757 function time24($t = false){
758 if($t === false) $t = date('Y-m-d H:i:s');
759 return date('H:i:s', strtotime($t));
760 }
761 ########################################################################
762 function time12($t = false){
763 if($t === false) $t = date('Y-m-d H:i:s');
764 return date('h:i:s A', strtotime($t));
765 }
766 ########################################################################
767 function application_url($page = '', $s = false){
768 if($s === false) $s = $_SERVER;
769 $ssl = (!empty($s['HTTPS']) && $s['HTTPS'] == 'on');
770 $http = ($ssl ? 'https:' : 'http:');
771 $port = $s['SERVER_PORT'];
772 $port = ((!$ssl && $port == '80') || ($ssl && $port == '443')) ? '' : ':' . $port;
773 $host = (isset($s['HTTP_HOST']) ? $s['HTTP_HOST'] : $s['SERVER_NAME'] . $port);
774 $uri = dirname($s['SCRIPT_NAME']);
775
776 /* app folder name (without the ending /admin part) */
777 $app_folder_is_admin = false;
778 $app_folder = substr(dirname(__FILE__), 0, -6);
779 if(substr($app_folder, -6, 6) == '/admin' || substr($app_folder, -6, 6) == '\\admin')
780 $app_folder_is_admin = true;
781
782 if(substr($uri, -12, 12) == '/admin/admin') $uri = substr($uri, 0, -6);
783 elseif(substr($uri, -6, 6) == '/admin' && !$app_folder_is_admin) $uri = substr($uri, 0, -6);
784 elseif($uri == '/' || $uri == '\\') $uri = '';
785
786 return "{$http}//{$host}{$uri}/{$page}";
787 }
788 ########################################################################
789 function is_ajax(){
790 return (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
791 }
792 ########################################################################
793 function array_trim($arr){
794 if(!is_array($arr)) return trim($arr);
795 return array_map('array_trim', $arr);
796 }
797 ########################################################################
798 function is_allowed_username($username, $exception = false){
799 $username = trim(strtolower($username));
800 if(!preg_match('/^[a-z0-9][a-z0-9 _.@]{3,19}$/', $username) || preg_match('/(@@| |\.\.|___)/', $username)) return false;
801
802 if($username == $exception) return $username;
803
804 if(sqlValue("select count(1) from membership_users where lcase(memberID)='{$username}'")) return false;
805 return $username;
806 }
807 ########################################################################
808 /*
809 if called without parameters, looks for a non-expired token in the user's session (or creates one if
810 none found) and returns html code to insert into the form to be protected.
811
812 if set to true, validates token sent in $_REQUEST against that stored in the session
813 and returns true if valid or false if invalid, absent or expired.
814
815 usage:
816 1. in a new form that needs csrf proofing: echo csrf_token();
817 >> in case of ajax requests and similar, retrieve token directly
818 by calling csrf_token(false, true);
819 2. when validating a submitted form: if(!csrf_token(true)){ reject_submission_somehow(); }
820 */
821 function csrf_token($validate = false, $token_only = false){
822 $token_age = 60 * 60;
823 /* retrieve token from session */
824 $csrf_token = (isset($_SESSION['csrf_token']) ? $_SESSION['csrf_token'] : false);
825 $csrf_token_expiry = (isset($_SESSION['csrf_token_expiry']) ? $_SESSION['csrf_token_expiry'] : false);
826
827 if(!$validate){
828 /* create a new token if necessary */
829 if($csrf_token_expiry < time() || !$csrf_token){
830 $csrf_token = md5(uniqid(rand(), true));
831 $csrf_token_expiry = time() + $token_age;
832 $_SESSION['csrf_token'] = $csrf_token;
833 $_SESSION['csrf_token_expiry'] = $csrf_token_expiry;
834 }
835
836 if($token_only) return $csrf_token;
837 return '<input type="hidden" id="csrf_token" name="csrf_token" value="' . $csrf_token . '">';
838 }
839
840 /* validate submitted token */
841 $user_token = (isset($_REQUEST['csrf_token']) ? $_REQUEST['csrf_token'] : false);
842 if($csrf_token_expiry < time() || !$user_token || $user_token != $csrf_token){
843 return false;
844 }
845
846 return true;
847 }
848 ########################################################################
849 function get_plugins(){
850 $plugins = array();
851 $plugins_path = dirname(__FILE__) . '/../plugins/';
852
853 if(!is_dir($plugins_path)) return $plugins;
854
855 $pd = dir($plugins_path);
856 while(false !== ($plugin = $pd->read())){
857 if(!is_dir($plugins_path . $plugin) || in_array($plugin, array('projects', 'plugins-resources', '.', '..'))) continue;
858
859 $info_file = "{$plugins_path}{$plugin}/plugin-info.json";
860 if(!is_file($info_file)) continue;
861
862 $plugins[] = json_decode(file_get_contents($info_file), true);
863 $plugins[count($plugins) - 1]['admin_path'] = "../plugins/{$plugin}";
864 }
865 $pd->close();
866
867 return $plugins;
868 }
869 ########################################################################
870 function maintenance_mode($new_status = ''){
871 $maintenance_file = dirname(__FILE__) . '/.maintenance';
872
873 if($new_status === true){
874 /* turn on maintenance mode */
875 @touch($maintenance_file);
876 }elseif($new_status === false){
877 /* turn off maintenance mode */
878 @unlink($maintenance_file);
879 }
880
881 /* return current maintenance mode status */
882 return is_file($maintenance_file);
883 }
884 ########################################################################
885 function handle_maintenance($echo = false){
886 if(!maintenance_mode()) return;
887
888 global $Translation;
889 $adminConfig = config('adminConfig');
890
891 $admin = getLoggedAdmin();
892 if($admin){
893 return ($echo ? '<div class="alert alert-danger" style="margin: 5em auto -5em;"><b>' . $Translation['maintenance mode admin notification'] . '</b></div>' : '');
894 }
895
896 if(!$echo) exit;
897
898 exit('<div class="alert alert-danger" style="margin-top: 5em; font-size: 2em;"><i class="glyphicon glyphicon-exclamation-sign"></i> ' . $adminConfig['maintenance_mode_message'] . '</div>');
899 }
900 #########################################################
901 function html_attr($str){
902 return htmlspecialchars($str, ENT_QUOTES, datalist_db_encoding);
903 }
904 #########################################################
905 class Request{
906 var $sql, $url, $attr, $html, $raw;
907
908 function __construct($var, $filter = false){
909 $this->Request($var, $filter);
910 }
911
912 function Request($var, $filter = false){
913 $unsafe = (isset($_REQUEST[$var]) ? $_REQUEST[$var] : '');
914 if(get_magic_quotes_gpc()) $unsafe = stripslashes($unsafe);
915
916 if($filter){
917 $unsafe = call_user_func($filter, $unsafe);
918 }
919
920 $this->sql = makeSafe($unsafe, false);
921 $this->url = urlencode($unsafe);
922 $this->attr = html_attr($unsafe);
923 $this->html = html_attr($unsafe);
924 $this->raw = $unsafe;
925 }
926 }
927 #########################################################
928 class Notification{
929 /*
930 Usage:
931 * in the main document, initiate notifications support using this PHP code:
932 echo Notification::placeholder();
933
934 * whenever you want to show a notifcation, use this PHP code:
935 echo Notification::show(array(
936 'message' => 'Notification text to display',
937 'class' => 'danger', // or other bootstrap state cues, 'default' if not provided
938 'dismiss_seconds' => 5, // optional auto-dismiss after x seconds
939 'dismiss_days' => 7, // optional dismiss for x days if closed by user -- must provide an id
940 'id' => 'xyz' // optional string to identify the notification -- must use for 'dismiss_days' to work
941 ));
942 */
943 protected static $placeholder_id; /* to force a single notifcation placeholder */
944
945 protected function __construct(){} /* to prevent initialization */
946
947 public static function placeholder(){
948 if(self::$placeholder_id) return ''; // output placeholder code only once
949
950 self::$placeholder_id = 'notifcation-placeholder-' . rand(10000000, 99999999);
951
952 ob_start();
953 ?>
954
955 <div class="notifcation-placeholder" id="<?php echo self::$placeholder_id; ?>"></div>
956 <script>
957 $j(function(){
958 if(window.show_notification != undefined) return;
959
960 window.show_notification = function(options){
961 /* wait till all dependencies ready */
962 if(window.notifications_ready == undefined){
963 var op = options;
964 setTimeout(function(){ show_notification(op); }, 20);
965 return;
966 }
967
968 var dismiss_class = '';
969 var dismiss_icon = '';
970 var cookie_name = 'hide_notification_' + options.id;
971 var notif_id = 'notifcation-' + Math.ceil(Math.random() * 1000000);
972
973 /* apply provided notficiation id if unique in page */
974 if(options.id != undefined){
975 if(!$j('#' + options.id).length) notif_id = options.id;
976 }
977
978 /* notifcation should be hidden? */
979 if(Cookies.get(cookie_name) != undefined) return;
980
981 /* notification should be dismissable? */
982 if(options.dismiss_seconds > 0 || options.dismiss_days > 0){
983 dismiss_class = ' alert-dismissible';
984 dismiss_icon = '<button type="button" class="close" data-dismiss="alert">×</button>';
985 }
986
987 /* remove old dismissed notficiations */
988 $j('.alert-dismissible.invisible').remove();
989
990 /* append notification to notifications container */
991 $j(
992 '<div class="alert alert-' + options['class'] + dismiss_class + '" id="' + notif_id + '">' +
993 dismiss_icon +
994 options.message +
995 '</div>'
996 ).appendTo('#<?php echo self::$placeholder_id; ?>');
997
998 var this_notif = $j('#' + notif_id);
999
1000 /* dismiss after x seconds if requested */
1001 if(options.dismiss_seconds > 0){
1002 setTimeout(function(){ this_notif.addClass('invisible'); }, options.dismiss_seconds * 1000);
1003 }
1004
1005 /* dismiss for x days if requested and user dismisses it */
1006 if(options.dismiss_days > 0){
1007 var ex_days = options.dismiss_days;
1008 this_notif.on('closed.bs.alert', function(){
1009 /* set a cookie not to show this alert for ex_days */
1010 Cookies.set(cookie_name, '1', { expires: ex_days });
1011 });
1012 }
1013 }
1014
1015 /* cookies library already loaded? */
1016 if(undefined != window.Cookies){
1017 window.notifications_ready = true;
1018 return;
1019 }
1020
1021 /* load cookies library */
1022 $j.ajax({
1023 url: '<?php echo PREPEND_PATH; ?>resources/jscookie/js.cookie.js',
1024 dataType: 'script',
1025 cache: true,
1026 success: function(){ window.notifications_ready = true; }
1027 });
1028 })
1029 </script>
1030
1031 <?php
1032 $html = ob_get_contents();
1033 ob_end_clean();
1034
1035 return $html;
1036 }
1037
1038 protected static function default_options(&$options){
1039 if(!isset($options['message'])) $options['message'] = 'Notification::show() called without a message!';
1040
1041 if(!isset($options['class'])) $options['class'] = 'default';
1042
1043 if(!isset($options['dismiss_seconds']) || isset($options['dismiss_days'])) $options['dismiss_seconds'] = 0;
1044
1045 if(!isset($options['dismiss_days'])) $options['dismiss_days'] = 0;
1046 if(!isset($options['id'])){
1047 $options['id'] = 0;
1048 $options['dismiss_days'] = 0;
1049 }
1050 }
1051
1052 /**
1053 * @brief Notification::show($options) displays a notification
1054 *
1055 * @param $options assoc array
1056 *
1057 * @return html code for displaying the notifcation
1058 */
1059 public static function show($options = array()){
1060 self::default_options($options);
1061
1062 ob_start();
1063 ?>
1064 <script>
1065 $j(function(){
1066 show_notification(<?php echo json_encode($options); ?>);
1067 })
1068 </script>
1069 <?php
1070 $html = ob_get_contents();
1071 ob_end_clean();
1072
1073 return $html;
1074 }
1075 }
1076 #########################################################
1077 function sendmail($mail){
1078 if(!isset($mail['to'])) return 'No recipient defined';
1079 if(!isEmail($mail['to'])) return 'Invalid recipient email';
1080
1081 $mail['subject'] = isset($mail['subject']) ? $mail['subject'] : '';
1082 $mail['message'] = isset($mail['message']) ? $mail['message'] : '';
1083 $mail['name'] = isset($mail['name']) ? $mail['name'] : '';
1084 $mail['debug'] = isset($mail['debug']) ? min(4, max(0, intval($mail['debug']))) : 0;
1085
1086 $cfg = config('adminConfig');
1087 $smtp = ($cfg['mail_function'] == 'smtp');
1088
1089 if(!class_exists('PHPMailer')){
1090 $curr_dir = dirname(__FILE__);
1091 include("{$curr_dir}/../resources/PHPMailer/class.phpmailer.php");
1092 if($smtp) include("{$curr_dir}/../resources/PHPMailer/class.smtp.php");
1093 }
1094
1095 $pm = new PHPMailer;
1096 $pm->CharSet = datalist_db_encoding;
1097
1098 if($smtp){
1099 $pm->isSMTP();
1100 $pm->SMTPDebug = $mail['debug'];
1101 $pm->Debugoutput = 'html';
1102 $pm->Host = $cfg['smtp_server'];
1103 $pm->Port = $cfg['smtp_port'];
1104 $pm->SMTPAuth = true;
1105 $pm->SMTPSecure = $cfg['smtp_encryption'];
1106 $pm->Username = $cfg['smtp_user'];
1107 $pm->Password = $cfg['smtp_pass'];
1108 }
1109
1110 $pm->setFrom($cfg['senderEmail'], $cfg['senderName']);
1111 $pm->addAddress($mail['to'], $mail['name']);
1112 $pm->Subject = $mail['subject'];
1113
1114 /* if message already contains html tags, don't apply nl2br */
1115 if($mail['message'] == strip_tags($mail['message']))
1116 $mail['message'] = nl2br($mail['message']);
1117
1118 $pm->msgHTML($mail['message'], realpath("{$curr_dir}/.."));
1119
1120 /* if sendmail_handler(&$pm) is defined (in hooks/__global.php) */
1121 if(function_exists('sendmail_handler')) sendmail_handler($pm);
1122
1123 if(!$pm->send()) return $pm->ErrorInfo;
1124
1125 return true;
1126 }